package physicalobjects;

import forces.Force;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import simulationhandlers.Assembly;
import simulationhandlers.Simulation;



/**
 * Creates a mass for the simulator.
 * @author Robert C. Duvall
 * modified by Kathleen Lan, Seon Kang
 */
public class Mass implements PhysicalObject {
    // state
    private Point2D myCenter;
    private Force myVelocity;
    private Dimension mySize;
    private int myID;
    private double myMass;
    private Force myAcceleration;
    private Assembly myAssembly;
    private double myBounceForceMagnitude = 2;
    private int myNetBoundSizeChange = 0;
    private final int myMassDimension = 16;
    private final double myRightDirection = 0;
    private final double myUpDirection = 90;
    private final double myLeftDirection = 180;
    private final double myDownDirection = 270;

    /**
     * @param id 
     * @param x 
     * @param y 
     * @param mass 
     */
    public Mass (int id, double x, double y, double mass) {
        myAcceleration = new Force();
        myMass = mass;
        myID = id;
        setCenter(x, y);
        setVelocity(0, 0);
        mySize = new Dimension(myMassDimension, myMassDimension);
    }

    @Override
    public void paint (Graphics2D pen) {
        pen.setColor(Color.BLACK);
        pen.fillOval(getLeft(), getTop(), getSize().width, getSize().height);
    }

    @Override
    public void update (Simulation canvas, double dt) {
        applyForce(getBounce(canvas.getSize()));
        // convert force back into Mover's velocity
        getVelocity().sum(myAcceleration);
        myAcceleration.reset();
        // move mass by velocity
        myCenter.setLocation(myCenter.getX() + myVelocity.getXChange() * dt,
                myCenter.getY() + myVelocity.getYChange() * dt);
    }

    /**
     *
     * @param n 
     */
    public void updateBoundSizeChange (int n) {
        myNetBoundSizeChange += n;
    }

    /**
     * @param f
     *        force to be applied to this mass
     */
    public void applyForce (Force f) {
        myAcceleration.sum(f);
    }

    /**
     *
     * @param id 
     * @return
     */
    public boolean match (int id) {
        return myID == id;
    }

    private Force getBounce (Dimension bounds) {
        if (getLeft() < 0 - myNetBoundSizeChange) {
            return createBounceForce(myRightDirection, myBounceForceMagnitude, 
                        getSize().width / 2 - myNetBoundSizeChange, getCenter().getY());
        }
        else if (getRight() > bounds.width + myNetBoundSizeChange) {
            return createBounceForce(myLeftDirection, myBounceForceMagnitude, 
                    bounds.width + myNetBoundSizeChange - getSize().width / 2, getCenter().getY());
        }
        if (getTop() < 0 - myNetBoundSizeChange) {
            return createBounceForce(myDownDirection, myBounceForceMagnitude, 
                    getCenter().getX(), getSize().height / 2 - myNetBoundSizeChange);
        }
        else if (getBottom() > bounds.height + myNetBoundSizeChange) {
            return createBounceForce(myUpDirection, myBounceForceMagnitude, getCenter().getX(), 
                        bounds.height + myNetBoundSizeChange - getSize().height / 2);
        }
        return new Force();
    }

    /**
     * 
     * @param impulseDirection 
     * @param impulseMagnitude 
     * @param xPos 
     * @param yPos 
     * @return
     */
    private Force createBounceForce(double impulseDirection, double impulseMagnitude,
            double xPos, double yPos) {
        setCenter(xPos, yPos);
        Force impulse = new Force(impulseDirection, impulseMagnitude);
        impulse.scale(getVelocity().getRelativeMagnitude(impulse));
        return impulse;
    }

    /**
     * Returns shape's velocity.
     */
    public Force getVelocity () {
        return myVelocity;
    }

    /**
     * Resets shape's velocity.
     * @param direction 
     * @param magnitude 
     */
    public void setVelocity (double direction, double magnitude) {
        myVelocity = new Force(direction, magnitude);
    }

    /**
     * Returns shape's center.
     */
    public Point2D getCenter () {
        return myCenter;
    }

    /**
     * Resets shape's center.
     * @param x 
     * @param y 
     */
    public void setCenter (double x, double y) {
        myCenter = new Point2D.Double(x, y);
    }

    /**
     * Returns shape's left-most coordinate.
     */
    public int getLeft () {
        return (int) (getCenter().getX() - getSize().width / 2);
    }

    /**
     * Returns shape's top-most coordinate.
     */
    public int getTop () {
        return (int) (getCenter().getY() - getSize().height / 2);
    }

    /**
     * Returns shape's right-most coordinate.
     */
    public int getRight () {
        return (int) (getCenter().getX() + getSize().width / 2);
    }

    /**
     * Reports shape's bottom-most coordinate.
     * 
     * @return bottom-most coordinate
     */
    public int getBottom () {
        return (int) (getCenter().getY() + getSize().height / 2);
    }

    /**
     * Returns shape's size.
     */
    public Dimension getSize () {
        return mySize;
    }
    /**
     * 
     * @return
     */
    public double getMass () {
        return myMass;
    }

    /**
     * 
     * @param a 
     */
    public void setAssembly (Assembly a) {
        myAssembly = a;
    }

    /**
     * 
     * @return
     */
    public Point2D getCenterOfMass () {
        return myAssembly.calculateCenterOfMass();
    }
}
